package com.yjzx.util.common.util.map;

import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Supplier;

/**
 * @author yjzx
 * @date 2023/5/9
 * @Description 缓存map，线程安全的
 */
public class MyRefMap<T> implements Map<Object, T> {
    private volatile ConcurrentHashMap<Object, Reference<T>> map = new ConcurrentHashMap<>(16);
    /**
     * 开启null有默认值
     */
    final Supplier<T> NULL_VALUE_FUN;
    /**
     * 是否开启基本数据类型同值同key,1==1L
     */
    private final boolean NUMERIC_TYPES;

    MyRefMap(Supplier<T> nullValueFun, boolean numericTypes) {
        this.NULL_VALUE_FUN = nullValueFun;
        this.NUMERIC_TYPES = numericTypes;
    }

    /**
     * 返回一个缓存map，可开启数字同值同key
     * @param nullValue value为null的默认值
     * @param numericTypes 是否开启数字同值同key
     * @param <T>
     * @return
     */
    public static <T> MyRefMap<T> getRef(T nullValue, boolean numericTypes) {
        return new MyRefMap<>(()->nullValue, numericTypes);
    }
    /**
     * 返回一个缓存map，可开启数字同值同key
     * @param nullValueFun value为null的默认值，null的默认值可为指定函数返回值
     * @param numericTypes 是否开启数字同值同key
     * @param <T>
     * @return
     */
    public static <T> MyRefMap<T> getRef(Supplier<T> nullValueFun, boolean numericTypes) {
        return new MyRefMap<>(nullValueFun, numericTypes);
    }

    public static <T> MyRefMap<T> getRef(boolean numericTypes) {
        return new MyRefMap<>(()->null, numericTypes);
    }

    ConcurrentHashMap<Object, Reference<T>> toRefMap() {
        return map;
    }

    private Reference<T> getNullValue() {
        return new WeakReference<>(NULL_VALUE_FUN.get());
    }

    /**
     * 对普通数值同值同key
     *
     * @param key
     * @return
     */
    private Object transKey(Object key) {
        Object k;
        if (NUMERIC_TYPES && key instanceof Number) {
            if (key instanceof Integer ||
                    key instanceof Long ||
                    key instanceof Byte ||
                    key instanceof Short) {
                k = ((Number) key).longValue();
            } else if (key instanceof Double ||
                    key instanceof Float) {
                k = ((Number) key).doubleValue();
            } else {
                k = key;
            }
        } else {
            k = key;
        }
        return k;
    }

    private <V extends Reference<?>> Reference<T> newInstance(Class<V> clazz, T t) {
        Reference<T> reference;
        try {
            if (WeakReference.class.equals(clazz)) {
                return new WeakReference<>(t);
            } else if (SoftReference.class.equals(clazz)) {
                return new SoftReference<>(t);
            }
            Class<?> refClazz = clazz;
            if (Modifier.isAbstract(refClazz.getModifiers())) {
                refClazz = WeakReference.class;
            }
            Constructor<?> constructor = refClazz.getConstructor(Object.class);
            constructor.setAccessible(true);
            reference = (Reference<T>) constructor.newInstance(t);
        } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        return reference;
    }

    public T put(Object key, T value, Class<? extends Reference> clazz) {
        T t;
        if (SoftReference.class.equals(clazz)) {
            t = putSoft(key, value);
        } else if (WeakReference.class.equals(clazz)) {
            t = putWeak(key, value);
        } else {
            t = put(key, newInstance(clazz, value));
        }
        return t;
    }

    /**
     * 软缓存，
     *
     * @param key
     * @param value
     * @return
     */
    public T putSoft(Object key, T value) {
        return put(key, new SoftReference<>(value));
    }

    /**
     * 弱缓存，gc就回收
     *
     * @param key
     * @param value
     * @return
     */
    public T putWeak(Object key, T value) {
        return put(key, new WeakReference<>(value));
    }

    /**
     * 可将所有reference传入
     *
     * @param key
     * @param value
     * @return
     */
    public T put(Object key, Reference<T> value) {
        T t = null;
        if (key != null) {
            key = transKey(key);
            map.put(key, value != null ?
                    (t = value.get()) == null ? getNullValue() : value
                    : getNullValue());
        }
        return t;
    }

    public void remove(Object... keys) {
        if (keys != null) {
            for (Object key : keys) {
                remove(key);
            }
        }
    }

    public void removeAll() {
        map = new ConcurrentHashMap<>(16);
    }

    @Override
    public T get(Object key) {
        key = transKey(key);
        if (key != null) {
            Reference<T> reference = map.get(key);
            if (reference != null) {
                return reference.get();
            } else {
                remove(key);
            }
        }
        return null;
    }

    @Override
    public T put(Object key, T value) {
        return put(key, value, WeakReference.class);
    }

    @Override
    public T remove(Object key) {
        if (key != null) {
            key = transKey(key);
            Reference<T> remove = map.remove(key);
            return remove != null ? remove.get() : null;
        }
        return null;
    }

    @Override
    public int size() {
        return map.size();
    }

    @Override
    public boolean isEmpty() {
        return map.isEmpty();
    }

    @Override
    public boolean containsKey(Object key) {
        return map.containsKey(key);
    }

    @Override
    public boolean containsValue(Object value) {
        return map.containsValue(value);
    }

    @Override
    public void putAll(Map<?, ? extends T> m) {
        m.forEach(this::put);
    }

    @Override
    public void clear() {
        map.clear();
    }

    @Override
    public Set<Object> keySet() {
        return map.keySet();
    }

    @Override
    public Collection<T> values() {
        return new ValuesView<>(this);
    }

    @Override
    public Set<Entry<Object, T>> entrySet() {
        return new EntryView(this);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        MyRefMap<?> that = (MyRefMap<?>) o;

        if (NUMERIC_TYPES != that.NUMERIC_TYPES) {
            return false;
        }
        if (map != null ? !map.equals(that.map) : that.map != null) {
            return false;
        }

        return NULL_VALUE_FUN.get() != null ? Objects.equals(NULL_VALUE_FUN.get(), that.NULL_VALUE_FUN.get()) : that.NULL_VALUE_FUN.get() == null;
    }

    @Override
    public int hashCode() {
        int result = map != null ? map.hashCode() : 0;
        T t = NULL_VALUE_FUN.get();
        result = 31 * result + (t != null ? t.hashCode() : 0);
        result = 31 * result + (NUMERIC_TYPES ? 1 : 0);
        return result;
    }

    static final class ValuesView<T> extends CollectionView<Object, Reference<T>, T> {
        private final ConcurrentHashMap<Object, Reference<T>> map;

        ValuesView(MyRefMap<T> map) {
            super(map.map);
            this.map = map.map;
        }

        @Override
        public Iterator<T> iterator() {
            return new ValuesIterator<T>(map);
        }

        @Override
        public boolean add(Object o) {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean contains(Object o) {
            return map.containsValue(o);
        }

        @Override
        public boolean remove(Object o) {
            if (o != null) {
                for (Iterator<T> it = iterator(); it.hasNext(); ) {
                    if (o.equals(it.next())) {
                        it.remove();
                        return true;
                    }
                }
            }
            return false;
        }

        @Override
        public boolean addAll(Collection c) {
            throw new UnsupportedOperationException();
        }

    }

    static final class EntryView<T> extends CollectionView<Object, Reference<T>, EntryNode<T>> implements Set<EntryNode<T>> {
        private final ConcurrentHashMap<Object, Reference<T>> map;

        EntryView(MyRefMap<T> map) {
            super(map.map);
            this.map = map.map;
        }

        @Override
        public Iterator<EntryNode<T>> iterator() {
            return new EntryIterator<>(map);
        }

        @Override
        public boolean add(EntryNode<T> objectTEntry) {
            return map.put(objectTEntry.getKey(), new WeakReference<>(objectTEntry.getValue())) == null;
        }

        @Override
        public boolean contains(Object o) {
            return map.contains(o);
        }

        @Override
        public boolean remove(Object o) {
            if (o != null) {
                for (Iterator<EntryNode<T>> it = iterator(); it.hasNext(); ) {
                    if (o.equals(it.next())) {
                        it.remove();
                        return true;
                    }
                }
            }
            return false;
        }

        @Override
        public boolean addAll(Collection<? extends EntryNode<T>> c) {
            boolean added = false;
            for (EntryNode<T> e : c) {
                if (add(e)) {
                    added = true;
                }
            }
            return added;
        }

    }

    static final class ValuesIterator<T> implements Iterator<T> {
        private final Iterator<Reference<T>> iterator;
        private T nextEntry;

        ValuesIterator(Map<Object, Reference<T>> map) {
            this.iterator = map.values().iterator();
        }

        @Override
        public boolean hasNext() {
            while (iterator.hasNext()) {
                Reference<T> entry = iterator.next();
                T value = entry.get();

                if (value != null) {
                    nextEntry = value;
                    return true;
                } else {
                    // 如果引用已经被回收，移除该条目
                    iterator.remove();
                }
            }
            return false;
        }

        @Override
        public T next() {
            if (nextEntry == null && !hasNext()) {
                throw new java.util.NoSuchElementException();
            }
            T result = nextEntry;
            nextEntry = null;
            return result;
        }

        @Override
        public void remove() {
            if (nextEntry == null) {
                throw new IllegalStateException();
            }
            iterator.remove();
            nextEntry = null;
        }
    }

    static final class EntryIterator<T> implements Iterator<EntryNode<T>> {
        private final Iterator<Map.Entry<Object, Reference<T>>> iterator;
        private EntryNode<T> nextEntry;


        EntryIterator(Map<Object, Reference<T>> map) {
            this.iterator = map.entrySet().iterator();
        }

        @Override
        public boolean hasNext() {
            while (iterator.hasNext()) {

                Entry<Object, java.lang.ref.Reference<T>> entry = iterator.next();
                T value = entry.getValue().get();
                if (value != null) {
                    nextEntry = new EntryNode<>(entry);
                    return true;
                } else {
                    // 如果引用已经被回收，移除该条目
                    iterator.remove();
                }
            }
            return false;
        }

        @Override
        public EntryNode<T> next() {
            if (nextEntry == null && !hasNext()) {
                throw new java.util.NoSuchElementException();
            }
            EntryNode<T> result = nextEntry;
            nextEntry = null;
            return result;
        }

        @Override
        public void remove() {
            if (nextEntry == null) {
                throw new IllegalStateException();
            }
            iterator.remove();
            nextEntry = null;
        }
    }

    static class EntryNode<T> implements MyEntry<Object, T> {
        private final Map.Entry<Object, Reference<T>> entry;


        public EntryNode(Map.Entry<Object, Reference<T>> entry) {
            this.entry = entry;

        }

        @Override
        public Entry getEntry() {
            return entry;
        }

        @Override
        public Object getKey() {
            return entry.getKey();
        }

        @Override
        public T getValue() {
            Reference<T> value = entry.getValue();
            return value == null ? null : value.get();
        }

        @Override
        public T setValue(T value) {
            entry.setValue(new WeakReference<>(value));
            return value;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            EntryNode entryNode = (EntryNode) o;

            return entry != null ? entry.equals(entryNode.entry) : entryNode.entry == null;
        }

        @Override
        public int hashCode() {
            return entry != null ? entry.hashCode() : 0;
        }

        @Override
        public String toString() {
            return "EntryNode{" +
                    "key=" + getKey() +
                    ",value=" + getValue() +
                    '}';
        }

    }

    abstract static class CollectionView<K, V, E>
            implements Collection<E>, java.io.Serializable {
        private static final long serialVersionUID = 7249069246763182397L;
        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
        final ConcurrentHashMap<K, V> map;

        CollectionView(ConcurrentHashMap<K, V> map) {
            this.map = map;
        }

        public ConcurrentHashMap<K, V> getMap() {
            return map;
        }

        /**
         * Removes all of the elements from this view, by removing all
         * the mappings from the map backing this view.
         */
        @Override
        public final void clear() {
            map.clear();
        }

        @Override
        public final int size() {
            return map.size();
        }

        @Override
        public final boolean isEmpty() {
            return map.isEmpty();
        }

        // implementations below rely on concrete classes supplying these
        // abstract methods

        /**
         * Returns an iterator over the elements in this collection.
         *
         * <p>The returned iterator is
         * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
         *
         * @return an iterator over the elements in this collection
         */
        @Override
        public abstract Iterator<E> iterator();

        @Override
        public abstract boolean contains(Object o);

        @Override
        public abstract boolean remove(Object o);

        private static final String oomeMsg = "Required array size too large";

        @Override
        public Object[] toArray() {
            long sz = map.mappingCount();
            if (sz > MAX_ARRAY_SIZE) {
                throw new OutOfMemoryError(oomeMsg);
            }
            int n = (int) sz;
            Object[] r = new Object[n];
            int i = 0;
            for (E e : this) {
                if (i == n) {
                    if (n >= MAX_ARRAY_SIZE) {
                        throw new OutOfMemoryError(oomeMsg);
                    }
                    if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) {
                        n = MAX_ARRAY_SIZE;
                    } else {
                        n += (n >>> 1) + 1;
                    }
                    r = Arrays.copyOf(r, n);
                }
                r[i++] = e;
            }
            return (i == n) ? r : Arrays.copyOf(r, i);
        }

        @Override
        @SuppressWarnings("unchecked")
        public <T1> T1[] toArray(T1[] array) {
            long sz = map.mappingCount();
            if (sz > MAX_ARRAY_SIZE) {
                throw new OutOfMemoryError(oomeMsg);
            }
            int m = (int) sz;
            T1[] r = (array.length >= m) ? array :
                    (T1[]) java.lang.reflect.Array
                            .newInstance(array.getClass().getComponentType(), m);
            int n = r.length;
            int i = 0;
            for (E e : this) {
                if (i == n) {
                    if (n >= MAX_ARRAY_SIZE) {
                        throw new OutOfMemoryError(oomeMsg);
                    }
                    if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) {
                        n = MAX_ARRAY_SIZE;
                    } else {
                        n += (n >>> 1) + 1;
                    }
                    r = Arrays.copyOf(r, n);
                }
                r[i++] = (T1) e;
            }
            if (array == r && i < n) {
                r[i] = null; // null-terminate
                return r;
            }
            return (i == n) ? r : Arrays.copyOf(r, i);
        }

        /**
         * Returns a string representation of this collection.
         * The string representation consists of the string representations
         * of the collection's elements in the order they are returned by
         * its iterator, enclosed in square brackets ({@code "[]"}).
         * Adjacent elements are separated by the characters {@code ", "}
         * (comma and space).  Elements are converted to strings as by
         * {@link String#valueOf(Object)}.
         *
         * @return a string representation of this collection
         */
        @Override
        public final String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append('[');
            Iterator<E> it = iterator();
            if (it.hasNext()) {
                for (; ; ) {
                    Object e = it.next();
                    sb.append(e == this ? "(this Collection)" : e);
                    if (!it.hasNext()) {
                        break;
                    }
                    sb.append(',').append(' ');
                }
            }
            return sb.append(']').toString();
        }

        @Override
        public final boolean containsAll(Collection<?> c) {
            if (c != this) {
                for (Object e : c) {
                    if (e == null || !contains(e)) {
                        return false;
                    }
                }
            }
            return true;
        }

        @Override
        public final boolean removeAll(Collection<?> c) {
            if (c == null) {
                throw new NullPointerException();
            }
            boolean modified = false;
            for (Iterator<E> it = iterator(); it.hasNext(); ) {
                if (c.contains(it.next())) {
                    it.remove();
                    modified = true;
                }
            }
            return modified;
        }

        @Override
        public final boolean retainAll(Collection<?> c) {
            if (c == null) {
                throw new NullPointerException();
            }
            boolean modified = false;
            for (Iterator<E> it = iterator(); it.hasNext(); ) {
                if (!c.contains(it.next())) {
                    it.remove();
                    modified = true;
                }
            }
            return modified;
        }

    }
    /**
     * ***********************************************自动清理无用缓存信息*************************************************
     */
    /**
     * 是否清理
     */
    private volatile boolean recycle = false;
    /**
     * 缓存{@link MyRefMap}对象清理队列
     */
    private static final CopyOnWriteArrayList<Reference<MyRefMap<?>>> QUEUE = new CopyOnWriteArrayList<>();
    /**
     * 清理间隔秒
     */
    private static final long RECYCLE_TIME = 30;

    public boolean isRecycle() {
        return recycle;
    }

    /**
     * 添加当前对象的是否需要清理（使用完成后也可以清理）
     *
     * @param enable
     * @return
     */
    public MyRefMap<T> setRecycle(boolean enable) {
        if (recycle != enable) {
            recycle = enable;
            if (recycle) {
                QUEUE.add(new WeakReference<>(this));
            } else {
                delRefClear();
            }

        }
        return this;
    }

    /**
     * 删除当前对象的定时清除
     */
    void delRefClear() {
        for (Reference<?> ref : QUEUE.toArray(new Reference[0])) {
            if (ref.get() == this) {
                ref.clear();
                break;
            }
        }
    }


    static {
        Thread handler = new RefMapHandler("com.yjzx.util.common.util.map.MyRefMap clear");

        //修改优先级
        //handler.setPriority(Thread.MAX_PRIORITY);
        //设置为守护线程
        handler.setDaemon(true);
        handler.start();
    }

    private static class RefMapHandler extends Thread {
        RefMapHandler(String name) {
            super(name);
        }

        private volatile boolean isRunning = false;

        private void clearRefMap() {
            try {
                if (QUEUE.isEmpty()) {
                    return;
                }
                isRunning = true;
                Iterator<Reference<MyRefMap<?>>> iterator = QUEUE.iterator();
                List<Reference<MyRefMap<?>>> removes = new ArrayList<>();
                if (iterator.hasNext()) {
                    Reference<MyRefMap<?>> next = iterator.next();
                    MyRefMap<?> refMap = next.get();
                    if (refMap == null) {
                        removes.add(next);
                    } else {
                        refMap.entrySet().removeIf(entry -> entry.getValue() == null || MyEntry.of(entry).isClear());
                    }
                }
                if (!removes.isEmpty()) {
                    QUEUE.removeAll(removes);
                }
            } finally {
                isRunning = false;
            }


        }

        @Override
        public void run() {
            while (true) {
                try {
                    if (!QUEUE.isEmpty() && !isRunning) {
                        clearRefMap();
                    }
                    if(RECYCLE_TIME>0){
                        Thread.sleep(1000 * RECYCLE_TIME);
                    }else {
                        Thread.sleep(1000);
                    }
                } catch (Exception ignored) {

                }
            }
        }
    }
}
